﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Net.Sockets;
using System.Net;
using AutoFillCenter;
using android.bluebox.model;

namespace OfficeServer
{
    class MessageDispatcher
    {
        String _IEAttributes = "";
        /*
        int _IEPort = 0;
        String _IEIP = "";
         */
        IPEndPoint _IEEndPoint = null;
        Socket _iehandler = null;
        private MobileInfo _mobileInfo;
        private MainForm _form;

        public MobileInfo getMobileInfo()
        {
            return _mobileInfo;
        }

        public MessageDispatcher()
        {

        }

        public MessageDispatcher(MobileInfo info)
        {
            _mobileInfo = info;
            if (info == null)
                _mobileInfo = new MobileInfo();
        }

        public MessageDispatcher(MobileInfo info, MainForm form)
        {
            _mobileInfo = info;
            if (info == null)
                _mobileInfo = new MobileInfo();
            _form = form;
        }

        public String runImmediately(String str, Socket handler, MobileInfo info)
        {

            //#region testIE
            //// FOR TEST ONLY! SHOULD COMMENT THIS IN IMPLEMENT
            //_mobileInfo.Status = Constant.STATUS_CONNECTED;
            //_mobileInfo.LastConnect = DateTime.Now;
            //_mobileInfo.SessionId = "123";
            //_mobileInfo.CodeValidation = "123";
            ////Crypto31.createInstance("123");
            //// END FOR TEST
            //#endregion
            

            #region Initialize

            byte[] msg = null;                      //            
            //int requestTypeMessage = 0;             
            int responseTypeMessage = 0;            // return type message ([1]) to MobileIP
            String data = "";                       // encrypted data
            String message = "";                    // raw data -> unencrypted data
            String ss = "";
            String[] messageData = null;            // message data split 
            // [0] sessionID
            // [1] type of message ( CONSTANT.)
            // [2] tag: 
            // [3] ... <encyrpted data> e.g: username:vnkhang(base64)
            String m_sessionID = "";
            int m_typeMessage = 0;
            String m_tag = "";
            String pairCode = "";                   // paircode for establish secured connection
            IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
            #endregion

            #region process the raw message to get standard message
            str = str.Replace("<EOF>", "");             // trim the END OF MESSAGE tag
            // if has connected -> decrypt the message
            if (_mobileInfo.Status > 1)
            {
                //IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
                IPEndPoint localIpEndPoint = handler.LocalEndPoint as IPEndPoint;
                String remoteIP = remoteIpEndPoint.Address.ToString();
                //if (remoteIpEndPoint.Address.ToString() != localIpEndPoint.Address.ToString())
                // in case of send from IE Plugin
                if (!str.Contains(Constant.SEPERATOR_CHAR))
                {
                    String temp = Crypto31.decrypt(str);
                    if (temp != null)
                        str = temp;

                }
            }
            messageData = str.Split(Constant.SEPERATOR_CHAR);
            //pairCode = messageData[0].ToUpper();
            m_sessionID = messageData[0];
            if(messageData.Length>2)
                m_tag = messageData[2].Replace("tag:", "");          // raw tag
            msg = Base64.decode("", Base64.DEFAULT);
            m_tag = System.Text.Encoding.ASCII.GetString(msg);  // decode -> get real tag value

            try
            {
                m_typeMessage = int.Parse(messageData[1]);
            }
            catch (FormatException ex)
            {
                System.Diagnostics.Debug.WriteLine("Can not cast to int");
                return "-1";
            }
            #endregion

            switch (m_typeMessage)
            {
                case Constant.REQUESTWELCOME:
                    return processRequestWelcome(handler);
                    break;
                #region NOT CONNECTED
                case Constant.REQUESTCONNECTING:
                    return processRequestConnecting(handler);
                    break;
                case Constant.REQUESTPAIRCODE:
                    return processRequestPaircode(handler,messageData[2]);
                    break;
                #endregion
                #region CONNECTED                
                case Constant.LOGOUT:
                    if (m_sessionID != _mobileInfo.SessionId)
                        return "-1" + Constant.SEPERATOR_CHAR + "Wrong session id";
                    return processLogout();
                    break;
                case Constant.RESPONSECONTENT:      // FILL APPS
                    if (m_sessionID != _mobileInfo.SessionId)
                        return "-1" + Constant.SEPERATOR_CHAR + "Wrong session id";
                    if(checkTimeOut(handler))
                        return "0" + Constant.SEPERATOR_CHAR + "TIMEOUT";
                    _mobileInfo.LastConnect = DateTime.Now;
                    return processResponseContent(handler, messageData);
                    break;
                case Constant.REQUESTFIELD:
                    if (checkTimeOut(handler))
                        return "0" + Constant.SEPERATOR_CHAR + "TIMEOUT";
                    
                    return processRequestField(handler, str);
                    break;
                case Constant.REQUESTFILLFIELD:                             // IE -> forward only
                    if (checkTimeOut(handler))
                        return "0" + Constant.SEPERATOR_CHAR + "TIMEOUT";
                    return processRequestFillField(handler, str);                    
                    break;
                case Constant.RESPONSEFILLFIELD:
                    return processResponseFillField(handler,str);
                    break;
                    
                default:
                    break;
                #endregion
            }            

            return null;
        }

        public String processMessage(String str, Socket handler, MobileInfo info)
        {
            // FOR TEST ONLY! SHOULD COMMENT THIS IN IMPLEMENT
            _mobileInfo.Status = Constant.STATUS_CONNECTED;
            _mobileInfo.LastConnect = DateTime.Now;
            _mobileInfo.SessionId = "123";
            _mobileInfo.CodeValidation = "123";
            Crypto31.createInstance("123");
            // END FOR TEST

            byte[] msg = null;
            System.Diagnostics.Debug.WriteLine(str);
            int requestTypeMessage = 0;
            int responseTypeMessage = 0;
            String data = "";
            String message = "";
            String ss = "";
            String[] messageData = null;
            String pairCode = "";
            IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;

            str = str.Replace("<EOF>", "");
            if (_mobileInfo.Status > 1)
            {
                //IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
                IPEndPoint localIpEndPoint = handler.LocalEndPoint as IPEndPoint;
                String remoteIP = remoteIpEndPoint.Address.ToString();
                //if (remoteIpEndPoint.Address.ToString() != localIpEndPoint.Address.ToString())
                if (!str.Contains(Constant.SEPERATOR_CHAR))
                {
                    String temp = Crypto31.decrypt(str);
                    if (temp != null)
                        str = temp;
                }
            }


            messageData = str.Split(Constant.SEPERATOR_CHAR);
            pairCode = messageData[0].ToUpper();

            try
            {
                requestTypeMessage = int.Parse(messageData[1]);
            }
            catch (FormatException ex)
            {
                System.Diagnostics.Debug.WriteLine("Can not cast to int");
                return "-1";
            }


            /**
             * TEST SERVER. SERVER MUST RESPONSE
             */
            #region WELCOME MESSAGE ( JUST REPLY )
            if (requestTypeMessage == Constant.REQUESTWELCOME)
            {
                ss = Constant.SEPERATOR_CHAR + Constant.WELCOMECONNECTING.ToString();
                msg = Encoding.UTF8.GetBytes(ss);
                _mobileInfo.LastConnect = DateTime.Now;
                //IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
                remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;

                handler.Send(msg);
                return "0" + Constant.SEPERATOR_CHAR + remoteIpEndPoint.Address.ToString();
            }
            #endregion

            #region NOT CONNECTED
            if (_mobileInfo.Status != Constant.STATUS_CONNECTED)
            {
                if (requestTypeMessage == Constant.REQUESTFILLFIELD)
                {
                    Console.WriteLine("Return something here");
                    return "NOT FIND ANYTHING HERE";
                }
                else if (requestTypeMessage == Constant.REQUESTCONNECTING)
                {
                    _mobileInfo.CodeValidation = Util.RandomString(Constant.CONFIG_NUM_RAND_STRING);
                    _mobileInfo.Status = Constant.STATUS_WAIT_FOR_CODE;
                    _mobileInfo.LastConnect = DateTime.Now;
                    responseTypeMessage = Constant.RESPONSECONNECTING;
                    message = _mobileInfo.CodeValidation;
                }
                else if (_mobileInfo.Status == Constant.STATUS_WAIT_FOR_CODE && requestTypeMessage == Constant.REQUESTPAIRCODE)
                {
                    String md5_code = Crypto31.createMD5(_mobileInfo.CodeValidation);
                    if (md5_code == messageData[2])
                    {
                        //_mobileInfo.Status = Constant.STATUS_HALF_CONNECTED;  //
                        _mobileInfo.Status = Constant.STATUS_CONNECTED;
                        //IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
                        remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
                        _mobileInfo.Ip = remoteIpEndPoint.Address.ToString();
                        _mobileInfo.LastConnect = DateTime.Now;
                        System.Diagnostics.Debug.WriteLine(_mobileInfo.Ip);

                        responseTypeMessage = Constant.RESPONSEPAIRCODE;
                        message = _mobileInfo.Ip.ToString();
                        Crypto31.createInstance(md5_code);
                        _mobileInfo.SessionId = (new Random()).Next().ToString();
                    }
                }
                /* @khangvn
            else if (_mobileInfo.Status == Constant.STATUS_HALF_CONNECTED && requestTypeMessage != Constant.ENSURE && _mobileInfo.SessionId==pairCode)
            {
                _mobileInfo.Status = Constant.STATUS_CONNECTED;
            }
                 */
            }
            #endregion
            #region CONNECTED
            else
            {
                //if (pairCode != _mobileInfo.CodeValidation)                                
                if (requestTypeMessage == Constant.REQUESTFIELD)
                {
                    responseTypeMessage = Constant.RESPONSEFIELD;
                    // Send message to mobile to get info
                    //ClientSender sender = new ClientSender();
                    //ClientSender.SendMessage("");

                    for (int i = 3; i < messageData.Length; i++)
                    {
                        data += Constant.SEPERATOR_CHAR + messageData[i];
                    }
                    Console.WriteLine(data);
                }
                else if (pairCode != _mobileInfo.SessionId)
                {
                    return "-1" + Constant.SEPERATOR_CHAR + "Wrong session id";
                }
                _mobileInfo.LastConnect = DateTime.Now;
                if (requestTypeMessage == Constant.LOGOUT)
                {
                    _mobileInfo.Port = 0;
                    _mobileInfo.Ip = null;
                    _mobileInfo.SessionId = null;
                    _mobileInfo.Status = Constant.STATUS_NOT_CONNECTED;
                    _mobileInfo.LastConnect = DateTime.MinValue;
                    Console.WriteLine("Logout");
                    return "0" + Constant.SEPERATOR_CHAR + "logout";

                }

                if (DateTime.Now.Subtract(_mobileInfo.LastConnect).TotalMinutes > Constant.TIMEOUT)
                {
                    _mobileInfo.CodeValidation = Util.RandomString(Constant.CONFIG_NUM_RAND_STRING);
                    _mobileInfo.Status = Constant.STATUS_WAIT_FOR_CODE;
                    System.Diagnostics.Debug.WriteLine("TIMEOUT HERE :" + DateTime.Now.TimeOfDay + "_" + _mobileInfo.LastConnect.TimeOfDay);
                    responseTypeMessage = Constant.RESPONSECONNECTING;
                    //message = _mobileInfo.CodeValidation;

                    //message = "," + _mobileInfo.CodeValidation;                    
                }
                // FILL APPS
                if (requestTypeMessage == Constant.RESPONSECONTENT)
                {
                    int numAttribute = Int32.Parse(messageData[3]);
                    AutoFill autoFill = AutoFillFactory.getAutoFill(messageData[2]);
                    Byte[] temp = Base64.decode(messageData[4], Base64.DEFAULT);
                    String username = System.Text.Encoding.ASCII.GetString(temp);
                    String password = null;
                    if (numAttribute != 1)
                    {
                        temp = Base64.decode(messageData[5], Base64.DEFAULT);
                        password = System.Text.Encoding.ASCII.GetString(temp);
                    }

                    autoFill.Fill(username, password);
                }
            }
            #endregion

            #region Send Data to Phone and print to the screen
            // do response here            
            ss = _mobileInfo.SessionId + Constant.SEPERATOR_CHAR + responseTypeMessage + data;
            //if (_mobileInfo.Status == Constant.STATUS_HALF_CONNECTED && _mobileInfo.CodeValidation != null)
            if (_mobileInfo.Status == Constant.STATUS_CONNECTED && _mobileInfo.CodeValidation != null)
            {
                _form.addMessage(ss + Environment.NewLine, false); // for easy to add
                ss = Crypto31.encrypt(ss);
            }
            if (requestTypeMessage != Constant.ENSURE)
            {
                msg = Encoding.UTF8.GetBytes(ss);
                if (requestTypeMessage == Constant.REQUESTFILLFIELD && _mobileInfo.Status == Constant.STATUS_CONNECTED)
                {
                    IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(_mobileInfo.Ip), _mobileInfo.Port);
                    handler.SendTo(msg, remoteEP);
                }
                else
                    handler.Send(msg);
            }

            System.Diagnostics.Debug.WriteLine(ss);
            Console.WriteLine(ss);
            #endregion

            return requestTypeMessage.ToString() + Constant.SEPERATOR_CHAR + message;

            //return null;
        }

        private bool checkTimeOut(Socket handler){
            if (DateTime.Now.Subtract(_mobileInfo.LastConnect).TotalMinutes > Constant.TIMEOUT)
            {
                String data = "";
                _mobileInfo.SessionId = "";
                _mobileInfo.CodeValidation = Util.RandomString(Constant.CONFIG_NUM_RAND_STRING);
                _mobileInfo.Status = Constant.STATUS_WAIT_FOR_CODE;
                System.Diagnostics.Debug.WriteLine("TIMEOUT HERE :" + DateTime.Now.TimeOfDay + "_" + _mobileInfo.LastConnect.TimeOfDay);
                int responseTypeMessage = Constant.RESPONSECONNECTING;
                String ss = _mobileInfo.SessionId + Constant.SEPERATOR_CHAR + responseTypeMessage + data;
                Byte[] msg = Encoding.UTF8.GetBytes(ss);
                handler.Send(msg);
                return true;
            }
            return false;
        }

        private String processResponseFillField(Socket handler,String message)
        {
            //IPEndPoint remoteEP = new IPEndPoint(, _IEPort);
            int index = message.IndexOf(Constant.SEPERATOR_CHAR);
            index = message.IndexOf(Constant.SEPERATOR_CHAR, index+1);
            String responseTypeMessage = Constant.RESPONSEFIELD.ToString();
            String data = message.Substring(index + 1);
            String ss = _mobileInfo.SessionId + Constant.SEPERATOR_CHAR + responseTypeMessage+ Constant.SEPERATOR_CHAR + data;
            Byte[] msg = Encoding.UTF8.GetBytes(ss);
            _iehandler.Send(msg);
            return null;
        }
        private String processRequestWelcome(Socket handler)
        {
            String ss = Constant.SEPERATOR_CHAR + Constant.WELCOMECONNECTING.ToString();
            Byte[] msg = Encoding.UTF8.GetBytes(ss);
            //_mobileInfo.LastConnect = DateTime.Now;                        
            IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;
            handler.Send(msg);
            return "0" + Constant.SEPERATOR_CHAR + remoteIpEndPoint.Address.ToString();            
        }
        private String processRequestConnecting(Socket handler)
        {
            String data = "";
            _mobileInfo.CodeValidation = Util.RandomString(Constant.CONFIG_NUM_RAND_STRING);
            _mobileInfo.Status = Constant.STATUS_WAIT_FOR_CODE;
            _mobileInfo.LastConnect = DateTime.Now;
            int responseTypeMessage = Constant.RESPONSECONNECTING;            

            String ss = _mobileInfo.SessionId + Constant.SEPERATOR_CHAR + responseTypeMessage + data;
            Byte[] msg = Encoding.UTF8.GetBytes(ss);
            handler.Send(msg);
            return Constant.REQUESTCONNECTING.ToString() + Constant.SEPERATOR_CHAR + _mobileInfo.CodeValidation;
        }
        private String processRequestPaircode(Socket handler,String receive_md5)
        {
            if (_mobileInfo.Status != Constant.STATUS_WAIT_FOR_CODE)
                return null;
            String md5_code = Crypto31.createMD5(_mobileInfo.CodeValidation);
            if (md5_code == receive_md5)
            {
                //_mobileInfo.Status = Constant.STATUS_HALF_CONNECTED;  
                String data = "";
                IPEndPoint remoteIpEndPoint = handler.RemoteEndPoint as IPEndPoint;

                // for replying the message
                int responseTypeMessage = Constant.RESPONSEPAIRCODE;                

                Crypto31.createInstance(md5_code);  // need Salt here
                _mobileInfo.SessionId = (new Random()).Next().ToString();
                _mobileInfo.Status = Constant.STATUS_CONNECTED;                
                _mobileInfo.Ip = remoteIpEndPoint.Address.ToString();
                _mobileInfo.Port = remoteIpEndPoint.Port;
                _mobileInfo.LastConnect = DateTime.Now;        
        
                String ss = _mobileInfo.SessionId + Constant.SEPERATOR_CHAR + responseTypeMessage + data;
                //String ss = "" + Constant.SEPERATOR_CHAR + responseTypeMessage + Crypto31.encrypt(_mobileInfo.SessionId);
                ss = Crypto31.encrypt(ss);
                Byte[] msg = Encoding.UTF8.GetBytes(ss);
                handler.Send(msg);
                return Constant.REQUESTPAIRCODE.ToString() + Constant.SEPERATOR_CHAR + _mobileInfo.Ip.ToString();
            }            
            return null;
        }
        private String processLogout()
        {
            _mobileInfo.Port = 0;
            _mobileInfo.Ip = null;
            _mobileInfo.SessionId = null;
            _mobileInfo.Status = Constant.STATUS_NOT_CONNECTED;
            _mobileInfo.LastConnect = DateTime.MinValue;
            Console.WriteLine("Logout");
            return "0" + Constant.SEPERATOR_CHAR + "logout";            
        }
        private String processResponseContent(Socket handler, String[] messageData)
        {
            //int numAttribute = Int32.Parse(messageData[3]);
            String tag = messageData[2].Replace("tag:", "");          // raw tag
            AutoFill autoFill = AutoFillFactory.getAutoFill(tag);
            AutoFill.IS_AUTO_SUBMIT = _form.getChkAutoSubmit();
            //Byte[] temp = Base64.decode(messageData[4], Base64.DEFAULT);
            //String username = System.Text.Encoding.ASCII.GetString(temp);
            //String password = null;
            String username = "";
            String password = "";
            for (int i = 3; i < messageData.Length; i++)
            {
                String[] values = messageData[i].Split(Constant.SEPERATOR_VALUE_CHAR);
                if (values[0] == "username")
                {
                    username = values[1];
                    continue;
                }
                else if (values[0] == "password")
                {
                    password = values[1];
                }
            }
            /*
            if (numAttribute != 1)
            {
                temp = Base64.decode(messageData[5], Base64.DEFAULT);
                password = System.Text.Encoding.ASCII.GetString(temp);
            }
             */ 
            autoFill.Fill(username, password);
            return Constant.RESPONSECONTENT.ToString() + Constant.SEPERATOR_CHAR;               
        }
        private String processRequestField(Socket handler, String message)
        {
            int index = message.IndexOf(Constant.SEPERATOR_CHAR);
            index = message.IndexOf(Constant.SEPERATOR_CHAR, index + 1);
            index = message.IndexOf(Constant.SEPERATOR_CHAR, index + 1);
            _IEAttributes = message.Substring(index + 1);
            _IEEndPoint = handler.RemoteEndPoint as IPEndPoint;
            _iehandler = handler;            

            //String encryrptedMsg = Crypto31.encrypt(message);
            //Byte[] msg = Encoding.UTF8.GetBytes(encryrptedMsg);
            //IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(_mobileInfo.Ip), _mobileInfo.Port);            
            //handler.SendTo(encryrptedMsg, remoteEP);

            //String res_message = "" + Constant.SEPERATOR_CHAR + Constant.RESPONSEFIELD + Constant.SEPERATOR_CHAR +
            //    "tag:home" + Constant.SEPERATOR_CHAR + "login[email]:khang.vonguyen@blueway.vn"
            //    + Constant.SEPERATOR_CHAR + "login[password]:concobebe";

            //String res_message = "" + Constant.SEPERATOR_CHAR + Constant.RESPONSEFIELD + Constant.SEPERATOR_CHAR +
            //    "tag:home" + Constant.SEPERATOR_CHAR + "login:khang.vonguyen@live.com"
            //    + Constant.SEPERATOR_CHAR + "passwd:deadoralive";            

            //String res_message = "" + Constant.SEPERATOR_CHAR + Constant.RESPONSEFIELD + Constant.SEPERATOR_CHAR +
            //"tag:home" + Constant.SEPERATOR_CHAR + "Email:khang.vonguyen@live.com"
            //+ Constant.SEPERATOR_CHAR + "Passwd:deadoralive";
            
            //String res_message = "" + Constant.SEPERATOR_CHAR + Constant.RESPONSEFIELD + Constant.SEPERATOR_CHAR +
            //    "tag:home" + Constant.SEPERATOR_CHAR + "login:dragoonk007"
            //    + Constant.SEPERATOR_CHAR + "passwd:deadoralive";
            
            //String res_message = "" + Constant.SEPERATOR_CHAR + Constant.RESPONSEFIELD + Constant.SEPERATOR_CHAR +
            //    "tag:home" + Constant.SEPERATOR_CHAR + "username:0612741"
            //    + Constant.SEPERATOR_CHAR + "password:deadoralive88";

            //Byte[] msg = Encoding.UTF8.GetBytes(res_message);
            //handler.Send(msg);
            return null;
        }
        private String processRequestFillField(Socket handler,String message)
        {
            //Byte[] msg = Encoding.UTF8.GetBytes(message);
            //IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(_mobileInfo.Ip), _mobileInfo.Port);
            //handler.SendTo(msg, remoteEP);
            //message.IndexOf("$", 0, 2);
            if (_IEAttributes == "")
            {
                return null;
            }
            String responseTypeMessage = Constant.REQUESTFILLFIELD.ToString();
            String data = _IEAttributes;
            String ss = _mobileInfo.SessionId + Constant.SEPERATOR_CHAR + responseTypeMessage + Constant.SEPERATOR_CHAR + data;
            //String ss = "" + Constant.SEPERATOR_CHAR + responseTypeMessage + Crypto31.encrypt(_mobileInfo.SessionId);
            ss = Crypto31.encrypt(ss);
            Byte[] msg = Encoding.UTF8.GetBytes(ss);
            handler.Send(msg);
            return Constant.REQUESTFILLFIELD + Constant.SEPERATOR_CHAR + _mobileInfo.Ip.ToString();
            return null;
        }

        
    }

}
